#include <iostream>
#include <deque>
using namespace std;
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
class Lockable : public boost::mutex
{
public:
	class Lock : public boost::unique_lock<boost::mutex>
	{
	public:
		Lock(boost::mutex& mutex)
			:boost::unique_lock<boost::mutex>(mutex)
		{
		}
	};
};
class AsyncObj : public Lockable 
{
public:
	void foo()
	{
		Lock lock(*this);
	//	boost::unique_lock<boost::mutex> lock(*this);
	}
};
const int maxProducts = 10;
boost::mutex printer;
void display(const string &str)
{
	boost::mutex::scoped_lock lock(printer);
	cout << str << endl;
}
class Shelf
{
public:
	boost::mutex mutex;
	boost::condition_variable_any cvGet, cvPut;
	void put(int i);
	void get(int i);
	bool isFull();
	bool isEmpty();
private:
	deque<string> products;
};
void Shelf::put(int i)
{
	stringstream ss;
	{
		boost::mutex::scoped_lock lock(mutex);
		while(isFull()) {
			ss << i << " **producer wait**";
			display(ss.str());
			cvPut.wait(mutex);
			ss.str("");
		}
		static int index;
		if (isFull()) {
			ss << i << " **puts nothing**";
		} else {
			ss << "item" << index++;
			string str = ss.str();
			products.push_back(str);
			ss.str("");
			ss << i << " puts " << str;
		}
	}
	display(ss.str());
	cvGet.notify_all();
}
void Shelf::get(int i)
{
	stringstream ss;
	{
		boost::mutex::scoped_lock lock(mutex);
		while(isEmpty()) {
			ss << i << " **consumer wait**";
			display(ss.str());
			cvGet.wait(mutex);
			ss.str("");
		}
		static int index;
		if (isEmpty()) {
			ss << i << " **gets nothing**";
		} else {
			string str = products.front();
			products.pop_front();
			ss << i << "\tgets " << str;
		}
	}
	display(ss.str());
	cvPut.notify_all();
}
bool Shelf::isFull()
{
		return products.size() >= maxProducts;
}
bool Shelf::isEmpty()
{
	return products.empty();
}
void producer(Shelf *shelf, int thread)
{
	while(1)
		shelf->put(thread);
}
void consumer(Shelf *shelf, int thread)
{
	while(1)
		shelf->get(thread);
}
int main(int argc, char* argv[])
{
	Shelf shelf;
 	boost::thread t1(boost::bind(producer, &shelf, 1));
 	boost::thread t2(boost::bind(consumer, &shelf, 1));	
	char ch;
	cin >> ch;
	cout << "stoping threads" << endl;
	t1.interrupt();
	t1.join();
	t2.interrupt();
	t2.join();
	return 0;
}

